package raft

import "sync/atomic"

//
// example RequestVote RPC arguments structure.
// field names must start with capital letters!
//
type RequestVoteArgs struct {
	// Your data here (2A, 2B).
	Term         int
	CandidateId  int
	LastLogIndex int
	LastLogTerm  int
}

//
// example RequestVote RPC reply structure.
// field names must start with capital letters!
//
type RequestVoteReply struct {
	// Your data here (2A).
	Term        int
	VoteGranted bool
}

//
// example RequestVote RPC handler.
//
func (rf *Raft) RequestVote(args *RequestVoteArgs, reply *RequestVoteReply) {
	// Your code here (2A, 2B).
	rf.mu.Lock()
	defer rf.mu.Unlock()

	// DPrintf("[%d] get vote request from %d", rf.me, args.CandidateId)

	reply.Term = rf.currentTerm

	if args.Term < rf.currentTerm {
		DPrintf("[%d] term is %v, %d candidate term is %v", rf.me, rf.currentTerm, args.CandidateId, args.Term)
		reply.VoteGranted = false
		return
	}

	if rf.currentTerm == args.Term && rf.votedFor != -1 && rf.votedFor != args.CandidateId {
		DPrintf("[%d] (currentTerm: %v, votedFor: %v) receive vote request from %d", rf.me, rf.currentTerm, rf.votedFor, args.CandidateId)
		reply.VoteGranted = false
		return
	}

	rf.currentTerm = args.Term

	if !rf.isUpdateToDate(args) {
		DPrintf("[%d] (lastLogTerm: %v lastLogIndex: %v) is more up to date than candidate %v (lastLogTerm: %v, lastLogIndex: %v)", rf.me, rf.getLastLogTerm(), rf.getLastLogIndex(), args.CandidateId, args.LastLogTerm, args.LastLogIndex)
		reply.VoteGranted = false
		rf.persist()
		return
	}

	rf.votedFor = args.CandidateId
	atomic.StoreInt32(&rf.state, Follower)
	rf.persist()
	reply.VoteGranted = true

	rf.tickerCh <- struct{}{}
}
